﻿global using Devonline.Core;
global using Devonline.Entity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;

namespace Devonline.Identity;

/// <summary>
/// 重载的身份上下文, 提供字符串类型主键的默认实现
/// </summary>
public class IdentityDbContext : IdentityDbContext<User, Role, UserClaim, UserRole, UserLogin, RoleClaim, UserToken, Group, UserGroup, Level, Resource, AccessRule, AccessApply, AccessRecord, Parameter, Attachment, string>
{
    public IdentityDbContext(DbContextOptions<IdentityDbContext> options) : base(options)
    {
    }
}

/// <summary>
/// 提供 TKey 作为主键类型的身份上下文
/// </summary>
/// <typeparam name="TKey"></typeparam>
public class IdentityDbContext<TKey> : IdentityDbContext<User<TKey>, Role<TKey>, UserClaim<TKey>, UserRole<TKey>, UserLogin<TKey>, RoleClaim<TKey>, UserToken<TKey>, Group<TKey>, UserGroup<TKey>, Level<TKey>, Resource<TKey>, AccessRule<TKey>, AccessApply<TKey>, AccessRecord<TKey>, Parameter<TKey>, Attachment<TKey>, TKey>
    where TKey : IEquatable<TKey>, IConvertible
{
    public IdentityDbContext(DbContextOptions<IdentityDbContext> options) : base(options)
    {
    }
}

/// <summary>
/// 重载的高级身份上下文
/// </summary>
/// <typeparam name="TUser"></typeparam>
/// <typeparam name="TRole"></typeparam>
/// <typeparam name="TUserClaim"></typeparam>
/// <typeparam name="TUserRole"></typeparam>
/// <typeparam name="TUserLogin"></typeparam>
/// <typeparam name="TRoleClaim"></typeparam>
/// <typeparam name="TUserToken"></typeparam>
/// <typeparam name="TGroup"></typeparam>
/// <typeparam name="TUserGroup"></typeparam>
/// <typeparam name="TLevel"></typeparam>
/// <typeparam name="TResource"></typeparam>
/// <typeparam name="TAccessRule"></typeparam>
/// <typeparam name="TAccessApply"></typeparam>
/// <typeparam name="TAccessRecord"></typeparam>
/// <typeparam name="TKey"></typeparam>
public class IdentityDbContext<TUser, TRole, TUserClaim, TUserRole, TUserLogin, TRoleClaim, TUserToken, TGroup, TUserGroup, TLevel, TResource, TAccessRule, TAccessApply, TAccessRecord, TParameter, TAttachment, TKey> :
    IdentityDbContext<TUser, TRole, TKey, TUserClaim, TUserRole, TUserLogin, TRoleClaim, TUserToken>
    where TUser : User<TKey>
    where TRole : Role<TKey>
    where TUserClaim : UserClaim<TKey>
    where TUserRole : UserRole<TKey>
    where TUserLogin : UserLogin<TKey>
    where TRoleClaim : RoleClaim<TKey>
    where TUserToken : UserToken<TKey>
    where TGroup : Group<TKey>
    where TUserGroup : UserGroup<TKey>
    where TLevel : Level<TKey>
    where TResource : Resource<TKey>
    where TAccessRule : AccessRule<TKey>
    where TAccessApply : AccessApply<TKey>
    where TAccessRecord : AccessRecord<TKey>
    where TParameter : Parameter<TKey>
    where TAttachment : Attachment<TKey>
    where TKey : IEquatable<TKey>, IConvertible
{
    public IdentityDbContext(DbContextOptions options) : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);

        builder.Entity<TUser>(b =>
        {
            b.HasKey(x => x.Id);
            b.ToTable(typeof(TUser).GetTableName());
            b.Property(x => x.State).HasDefaultValue(DataState.Available);
            b.Property(x => x.Type).HasDefaultValue(AuthorizeType.Internal);

            b.HasIndex(u => u.NormalizedUserName).HasDatabaseName("UserNameIndex").IsUnique();
            b.HasIndex(u => u.NormalizedEmail).HasDatabaseName("EmailIndex");

            b.HasMany<TUserRole>().WithOne().HasForeignKey(x => x.UserId).IsRequired();
            b.HasMany<TUserGroup>().WithOne().HasForeignKey(x => x.UserId).IsRequired();
            b.HasMany<TUserClaim>().WithOne().HasForeignKey(x => x.UserId).IsRequired();
            b.HasMany<TUserLogin>().WithOne().HasForeignKey(x => x.UserId).IsRequired();
            b.HasMany<TUserToken>().WithOne().HasForeignKey(x => x.UserId).IsRequired();
        });

        builder.Entity<TRole>(b =>
        {
            b.HasKey(x => x.Id);
            b.ToTable(typeof(TRole).GetTableName());
            b.Property(x => x.State).HasDefaultValue(DataState.Available);
            b.Property(x => x.Type).HasDefaultValue(AuthorizeType.Internal);

            b.HasIndex(x => x.NormalizedName).HasDatabaseName("RoleNameIndex").IsUnique();

            b.HasMany<TUserRole>().WithOne().HasForeignKey(x => x.RoleId).IsRequired();
            b.HasMany<TRoleClaim>().WithOne().HasForeignKey(x => x.RoleId).IsRequired();
            b.HasMany<TUserGroup>().WithOne().HasForeignKey(x => x.RoleId);
        });

        builder.Entity<TUserRole>(b =>
        {
            b.HasKey(x => new { x.UserId, x.RoleId });
            b.ToTable(typeof(TUserRole).GetTableName());
        });

        builder.Entity<TUserLogin>(b =>
        {
            b.HasKey(x => new { x.LoginProvider, x.ProviderKey });
            b.ToTable(typeof(TUserLogin).GetTableName());
        });

        builder.Entity<TUserToken>(b =>
        {
            b.HasKey(x => new { x.UserId, x.LoginProvider, x.Name });
            b.ToTable(typeof(TUserToken).GetTableName());
        });

        builder.Entity<TUserClaim>().ToTable(typeof(TUserClaim).GetTableName());
        builder.Entity<TRoleClaim>().ToTable(typeof(TRoleClaim).GetTableName());

        builder.Entity<TLevel>(b =>
        {
            b.Property(x => x.State).HasDefaultValue(DataState.Available);
            b.Property(x => x.Type).HasDefaultValue(AuthorizeType.Internal);
        });

        builder.Entity<TGroup>(b =>
        {
            b.Property(x => x.State).HasDefaultValue(DataState.Available);
            b.Property(x => x.Type).HasDefaultValue(AuthorizeType.Internal);
            b.HasMany<TUserGroup>().WithOne().HasForeignKey(x => x.GroupId).IsRequired();
        });

        builder.Entity<TUserGroup>(b => b.HasKey(x => new { x.UserId, x.GroupId }));
        builder.Entity<TResource>(b =>
        {
            b.Property(x => x.State).HasDefaultValue(DataState.Available);
            b.Property(x => x.Type).HasDefaultValue(AuthorizeType.Internal);
            b.Property(x => x.IdentityType).HasDefaultValue(IdentityType.User);
            b.Property(x => x.ResourceType).HasDefaultValue(ResourceType.Page);
            b.Property(x => x.AccessLevel).HasDefaultValue(AccessLevel.Internal);
        });

        builder.Entity<TAccessApply>(b =>
        {
            b.Property(x => x.State).HasDefaultValue(DataState.Available);
            b.Property(x => x.AllowType).HasDefaultValue(AllowType.Allow);
        });

        builder.Entity<TAccessRule>(b =>
        {
            b.Property(x => x.State).HasDefaultValue(DataState.Available);
            b.Property(x => x.IdentityType).HasDefaultValue(IdentityType.Role);
            b.Property(x => x.AllowType).HasDefaultValue(AllowType.Allow);
        });

        builder.Entity<TAccessRecord>(b => b.Property(x => x.State).HasDefaultValue(DataState.Available));
        builder.Entity<TParameter>(b => b.Property(x => x.State).HasDefaultValue(DataState.Available));
        builder.Entity<TAttachment>(b => b.Property(x => x.State).HasDefaultValue(DataState.Available));
    }

    #region identity core entities
    /// <summary>
    /// level
    /// </summary>
    public virtual DbSet<TLevel> Levels { get; set; }
    /// <summary>
    /// Group
    /// </summary>
    public virtual DbSet<TGroup> Groups { get; set; }
    /// <summary>
    /// UserGroup
    /// </summary>
    public virtual DbSet<TUserGroup> UserGroups { get; set; }
    /// <summary>
    /// Resource
    /// </summary>
    public virtual DbSet<TResource> Resources { get; set; }
    /// <summary>
    /// AccessRule
    /// </summary>
    public virtual DbSet<TAccessRule> AccessRules { get; set; }
    /// <summary>
    /// AccessApply
    /// </summary>
    public virtual DbSet<TAccessApply> AccessApplies { get; set; }
    /// <summary>
    /// AccessRecord
    /// </summary>
    public virtual DbSet<TAccessRecord> AccessRecords { get; set; }
    /// <summary>
    /// Parameter
    /// </summary>
    public virtual DbSet<TParameter> Parameters { get; set; }
    /// <summary>
    /// Attachment
    /// </summary>
    public virtual DbSet<TAttachment> Attachments { get; set; }
    #endregion
}